#include "StdAfx.h"
#if !defined(LINUX)
#include "FMODBankDecoder.h"

#include <CrySizer.h>
#include <ISystem.h>
#include <ICryPak.h>
#include <ITimer.h>
#include "../MusicSystem.h"
#include "../../SoundSystem.h"

void AddPhysicalBlock(long size);

CFMODBankDecoder::CFMODBankDecoder(IMusicSystem *pMusicSystem, CMusicPattern *pMusicPattern)
	: m_BankSound(NULL),
	  m_SubSound(NULL)
{
	m_nSubSoundIndex = 0;
	m_nMainThreadId = 0;
	m_pReadStream = 0;
	m_BufferData = 0;
	m_BufferLength = 0;
	m_pMusicSystem = (CMusicSystem*) gEnv->pMusicSystem;

	m_FileInfo.bStreamFromDisk = true;
	m_FileInfo.pMusicPattern = pMusicPattern;
}

CFMODBankDecoder::~CFMODBankDecoder()
{

	float f0 = 0.0f;
	float f1 = 0.0f;

	if (m_pMusicSystem->g_nMusicProfiling)
		f0 = gEnv->pTimer->GetAsyncCurTime();

	Close();

	assert(IsReleasable());
	
	if (m_BankSound)
	{
		FMOD_RESULT const eResult	= m_BankSound->release();
		m_BankSound								= NULL;
	}

	if (m_pReadStream != NULL)
	{
		m_pReadStream->Abort();	
		m_pReadStream = NULL;
	}

	if (m_BufferData != NULL)
	{
		bool bFreed = false;






			bFreed = g_MemoryPoolPrimary.Free(m_BufferData);

		m_BufferData = 0;
		m_BufferLength = 0;

	}

	if (m_pMusicSystem->g_nMusicProfiling)
	{
		f1 = gEnv->pTimer->GetAsyncCurTime();
		CryLog(" Profile: Decoder-Destruction %s %f", m_FileInfo.sFileName.c_str(), (f1-f0)*1000.0f);
	}
}

//////////////////////////////////////////////////////////////////////////
bool CFMODBankDecoder::OpenStream()
{
	FMOD::System *pSystem = (FMOD::System *)gEnv->pSoundSystem->GetIAudioDevice()->GetSoundLibrary(); // unsafe!
	FMOD_MODE Mode = FMOD_OPENONLY | FMOD_LOWMEM | FMOD_CREATESTREAM;
	FMOD_RESULT Result = FMOD_OK;
	FMOD_CREATESOUNDEXINFO  ExInfo = {0}; 
	ExInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
	ExInfo.length = m_BufferLength;
	ExInfo.suggestedsoundtype = FMOD_SOUND_TYPE_FSB;
	ExInfo.initialsubsound = 0;
	ExInfo.decodebuffersize = 4096*4;

	float f0, f1, f2, f3;

	if (m_pMusicSystem->g_nMusicProfiling)
		f0 = gEnv->pTimer->GetAsyncCurTime();

	if (m_BufferData == NULL)
	{
		// File gets streamed from disk
		if (m_BankSound == NULL)
		{
			CSoundSystem const* const pSoundSystem = (CSoundSystem*)gEnv->pSoundSystem;
			(pSoundSystem->g_nFileAccess > 0) ? Mode |= FMOD_NONBLOCKING : 0;

			FRAME_PROFILER( "FMOD-CreateSound Music FSB-Decoder", GetISystem(), PROFILE_MUSIC );
			//Result = pSystem->createSound(sBankFileName, Mode, 0, &m_BankSound);
			Result = pSystem->createSound(m_FileInfo.sFileName.c_str(), Mode, &ExInfo, &m_BankSound);
		}

	}
	else
	{
		// File is in Memory
		Mode |= FMOD_OPENMEMORY_POINT;
		{
			FRAME_PROFILER( "FMOD-CreateSound Music FSB-Decoder", GetISystem(), PROFILE_MUSIC );
			//Result = pSystem->createSound(sBankFileName, Mode, 0, &m_BankSound);
			Result = pSystem->createSound((const char*)m_BufferData, Mode, &ExInfo, &m_BankSound);
		}
	}

	if (m_pMusicSystem->g_nMusicProfiling)
	{
		f1 = gEnv->pTimer->GetAsyncCurTime();
		CryLog(" Profile: Decoder Open Stream ReadOnly Sound %s %f", m_FileInfo.sFileName.c_str(), (f1-f0)*1000.0f);
	}

	if (!m_BankSound)
	{
		gEnv->pSoundSystem->Log(eSLT_Error, "<Sound> Music - Cannot load FSB %s\n", m_FileInfo.sFileName.c_str());
		//m_Info.bLoadFailure = true;
		//LoadFailed();
		return false;
	}	

	if (Result != FMOD_OK || m_BankSound == NULL)
		return false;

	if (m_pMusicSystem->g_nMusicProfiling)
		f2 = gEnv->pTimer->GetAsyncCurTime();

	if (m_SubSound == NULL)
		Result = m_BankSound->getSubSound(m_nSubSoundIndex, &m_SubSound);

	if (IsSoundAccessible(m_SubSound))
	{
		Result = m_SubSound->getFormat(0, 0, &(m_FileInfo.nChannels), &(m_FileInfo.nBitsPerSample));
		Result = m_SubSound->getDefaults(&(m_FileInfo.fFrequency), 0, 0, 0);

		Result = m_SubSound->getLength(&(m_FileInfo.nSamples), FMOD_TIMEUNIT_PCM);
		Result = m_SubSound->getLength(&(m_FileInfo.nRawBytes), FMOD_TIMEUNIT_RAWBYTES);

		m_FileInfo.pMusicPattern->SetNumSamples(m_FileInfo.nSamples);

		assert (m_FileInfo.nChannels);
		assert (m_FileInfo.nBitsPerSample);
		assert (m_FileInfo.fFrequency);
		assert (m_FileInfo.nSamples);
		assert (m_FileInfo.nRawBytes);

		if ( !m_FileInfo.nChannels || !m_FileInfo.nBitsPerSample || !m_FileInfo.fFrequency || !m_FileInfo.nSamples || !m_FileInfo.nRawBytes )
		{
			int catchme = 1;
			(void)catchme;
		}
		if (m_pMusicSystem->g_nMusicProfiling)
		{
			f3 = gEnv->pTimer->GetAsyncCurTime();
			CryLog(" Profile: Decoder Open Stream ReadOnly SubSound %s %f", m_FileInfo.sFileName.c_str(), (f3-f2)*1000.0f);
		}

		// DO NOT CLOSE sound from other thread than main
		if (m_BufferData == NULL)
		{
			m_SubSound->release();
			m_SubSound = NULL;

			if (m_pMusicSystem->g_nMusicProfiling)
			{
				f3 = gEnv->pTimer->GetAsyncCurTime();
				CryLog(" Profile: Decoder Open Stream Shared BankSound %s %f", m_FileInfo.sFileName.c_str(), (f3-f2)*1000.0f);
			}
		}

		m_FileInfo.eAudioFileOpenState = eAFOS_READY;

		// Double check here if this patterns' fade points are set properly and don't exceed the file's actual length
		if (m_FileInfo.pMusicPattern)
		{
			size_t const nNumSamples	= (size_t)m_FileInfo.nSamples;
			TMarkerVec aoFadePoints		= m_FileInfo.pMusicPattern->GetFadePoints();

			TMarkerVecIt const ItFadePointsEnd = aoFadePoints.end();
			for (TMarkerVecIt ItFadePoints = aoFadePoints.begin(); ItFadePoints != ItFadePointsEnd; ++ItFadePoints)
			{
				size_t const nFadePointPos = (*ItFadePoints);

				if (nFadePointPos >= nNumSamples)
				{
					gEnv->pSoundSystem->Log(eSLT_Error, "<Sound> Music - Pattern does have fade points set beyond its actual length!\n");
					gEnv->pSoundSystem->Log(eSLT_Error, "<Sound> Music - Pattern Name: %s\n", m_FileInfo.sFileName.c_str());
					assert(0);
				}
			}
		}
	}
	else if (Result != FMOD_OK)
	{
		// Set the correct open state
		FMOD_OPENSTATE eOpenState;
		Result = m_BankSound->getOpenState(&eOpenState, NULL, NULL, NULL);

		switch (eOpenState)
		{
		case FMOD_OPENSTATE_LOADING:
			{
				m_FileInfo.eAudioFileOpenState = eAFOS_LOADING;
			}
			break;
		case FMOD_OPENSTATE_ERROR:
			{
				m_FileInfo.eAudioFileOpenState = eAFOS_ERROR;
			}
			break;
		}
	}
	
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CFMODBankDecoder::StreamOnComplete(IReadStream *pStream, unsigned nError)
{
	FUNCTION_PROFILER( gEnv->pSystem, PROFILE_MUSIC );
		
	MEMSTAT_CONTEXT_FMT(EMemStatContextTypes::MSC_FSB, 0, "Music FSB %s", m_FileInfo.sFileName.c_str() );
	
#ifdef FMOD_STREAMING_DEBUGGING
	{
		CryAutoCriticalSection SmartCriticalSection(m_CSDebug);
		--g_nStreamingDebugCount;
#ifdef FMOD_STREAMING_DEBUG_LOGGING
		gEnv->pSoundSystem->Log(eSLT_Always, "BD_SOC: %s (%d)", m_FileInfo.sFileName.c_str(), g_nStreamingDebugCount);
#endif // FMOD_STREAMING_DEBUG_LOGGING
	}
#endif // FMOD_STREAMING_DEBUGGING
	
	// Dont need stream anymore.
	m_pReadStream = NULL;

	if (nError)
	{
		gEnv->pSoundSystem->Log(eSLT_Error, "<Sound> Music - Cannot load FSB %s\n", m_FileInfo.sFileName.c_str());
		
		bool bFreed = false;





			bFreed = g_MemoryPoolPrimary.Free(m_BufferData);

		m_BufferData = NULL;

		return;
	}

	// make a local copy of the file and store it only once in the decoder
	//m_BufferLength = pStream->GetBytesRead();
	bool bResult = OpenStream();
}

bool CFMODBankDecoder::Open(const char *sBankFileName, const char *sSoundName)
{
	m_nSubSoundIndex = 0;

	m_BankSound = NULL;
	m_SubSound = NULL;
	bool bResult = true;

	m_FileInfo.sFileName = sBankFileName;
	m_FileInfo.sSoundName = sSoundName;	

	if (m_pMusicSystem->g_nMusicStreaming == 1 || (m_FileInfo.bStreamFromDisk && m_pMusicSystem->g_nMusicStreaming != 2))
	{
		// Stream from Disk
		bResult = OpenStream();
	}
	else
	{
		// Initiate Streaming request
		if (!m_pReadStream && !m_BankSound)
		{
			m_BufferLength = gEnv->pCryPak->FGetSize(sBankFileName);

			// file does not exist
			if (!m_BufferLength) 
				return false;






				m_BufferData = g_MemoryPoolPrimary.Allocate<void*>(m_BufferLength, CRYSOUNDMEMORY_ALLIGNMENT,"FMOD BankBuffer");

			if (m_BufferData)
			{
				StreamReadParams StrParams;
				StrParams.nFlags = IStreamEngine::FLAGS_IGNORE_TMP_OUT_OF_MEM;
				StrParams.dwUserData = 0;
				StrParams.nLoadTime = 0;
				StrParams.nMaxLoadTime = 0;
//				StrParams.nPriority = 0;
				StrParams.pBuffer = m_BufferData;
				StrParams.nOffset = 0;
				StrParams.nSize = m_BufferLength;

#ifdef FMOD_STREAMING_DEBUGGING
				{
					CryAutoCriticalSection SmartCriticalSection(m_CSDebug);
					++g_nStreamingDebugCount;
#ifdef FMOD_STREAMING_DEBUG_LOGGING
					gEnv->pSoundSystem->Log(eSLT_Always, "BD_READ_START: %s (%d)", sBankFileName, g_nStreamingDebugCount);
#endif // FMOD_STREAMING_DEBUG_LOGGING
				}
#endif // FMOD_STREAMING_DEBUGGING

				m_pReadStream = gEnv->pSystem->GetStreamEngine()->StartRead(eStreamTaskTypeMusic, sBankFileName, this, &StrParams);

				if (m_pReadStream == 0)
					return false;
			}
			else
			{
				// allocation failed, so lets try to stream as fallback
				bResult = OpenStream(); // TODO: verify reliability after introducing async music loading
			}
		}
	}


	return bResult;
}

bool CFMODBankDecoder::Close()
{
	float f0, f1;
	bool bLogMessage = false;

	if (m_pMusicSystem->g_nMusicProfiling)
		f0 = gEnv->pTimer->GetAsyncCurTime();

	FMOD_RESULT Result;
	
	if (m_SubSound)
	{
		Result			= m_SubSound->release();
		m_SubSound	= NULL;
	}

	bLogMessage = true;

	if (m_BufferData && m_BankSound)
	{
		Result			= m_BankSound->release();
		m_BankSound	= NULL;
		bLogMessage	= true;
	}

	if (m_pMusicSystem->g_nMusicProfiling && bLogMessage)
	{
		f1 = gEnv->pTimer->GetAsyncCurTime();
		CryLog(" Profile: Decoder-BankRelease %s %f", m_FileInfo.sFileName.c_str(), (f1-f0)*1000.0f);
	}

	return true;
}

bool CFMODBankDecoder::GetFileInfo(SMusicPatternFileInfo &FileInfo)
{

	FileInfo = m_FileInfo;

	return true;
}

void CFMODBankDecoder::GetMemoryUsage(class ICrySizer* pSizer)
{
	if (m_BufferData)
		pSizer->AddObject(m_BufferData, m_BufferLength);

	if (!pSizer->Add(*this))
		return;
}

//////////////////////////////////////////////////////////////////////////
bool const CFMODBankDecoder::IsReleasable() const
{
	FMOD_OPENSTATE eOpenState;
	bool bDiskBusy = false;

	if (m_BankSound)
	{
		FMOD_RESULT const eResult = m_BankSound->getOpenState(&eOpenState, NULL, NULL, &bDiskBusy);

		if ((eOpenState != FMOD_OPENSTATE_READY && eOpenState != FMOD_OPENSTATE_ERROR) || bDiskBusy)
			return false;
	}

	if (m_SubSound)
	{
		FMOD_RESULT const eResult = m_SubSound->getOpenState(&eOpenState, NULL, NULL, &bDiskBusy);

		if ((eOpenState != FMOD_OPENSTATE_READY && eOpenState != FMOD_OPENSTATE_ERROR) || bDiskBusy)
			return false;
	}

	return true;
}

IMusicPatternDecoderInstance* CFMODBankDecoder::CreateInstance()
{
	POOL_NEW_CREATE(CFMODBankDecoderInstance,pNewInstance)(this);
	return pNewInstance;
}

void CFMODBankDecoder::MainThreadUpdate(uint32 nMainThreadId)
{
	m_nMainThreadId = nMainThreadId;
	
	// We don't need Decoders sounds anymore.
	(m_FileInfo.eAudioFileOpenState != eAFOS_LOADING) ? Close() : OpenStream(); // Keep trying until the stream has finished
}

//////////////////////////////////////////////////////////////////////////
bool CFMODBankDecoder::IsSoundAccessible(FMOD::Sound* const pSound) const
{
	if (pSound)
	{
		FMOD_OPENSTATE eOpenState;
		FMOD_RESULT const eResult = pSound->getOpenState(&eOpenState, NULL, NULL, NULL);

		return (eOpenState == FMOD_OPENSTATE_READY || eOpenState == FMOD_OPENSTATE_ERROR);
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
bool CFMODBankDecoder::IsSoundSeekable(FMOD::Sound* const pSound) const
{
	if (pSound)
	{
		FMOD_OPENSTATE eOpenState;
		bool bDiskBusy = false;
		FMOD_RESULT const eResult = pSound->getOpenState(&eOpenState, NULL, NULL, &bDiskBusy);

		return ((eOpenState == FMOD_OPENSTATE_READY || eOpenState == FMOD_OPENSTATE_ERROR) && !bDiskBusy);
	}

	return false;
}

//////////////////////////////////////////////////////////////////////////
/*** INSTANCE ***/
//////////////////////////////////////////////////////////////////////////

CFMODBankDecoderInstance::CFMODBankDecoderInstance(CFMODBankDecoder *pDecoder)
:	m_bCopyFromLastFrame(false),
	m_eAudioFileOpenState(eAFOS_ERROR)
{
	m_pDecoder = pDecoder;
	m_BankSound = 0;
	m_SubStream = 0;
	m_nPosInSamples = 0;	
	m_nStreamPosInSamples = 0;
	m_nMainThreadId = 0;
	m_pReadStream = 0;

	m_pMusicSystem = (CMusicSystem*) gEnv->pMusicSystem;

	Init();

	Seek0();
}

CFMODBankDecoderInstance::~CFMODBankDecoderInstance()
{
	float f0, f1;
	if (m_pMusicSystem->g_nMusicProfiling)
		f0 = gEnv->pTimer->GetAsyncCurTime();

	FMOD_RESULT Result;

	assert(IsReleasable());

	// release of the SubStream on every Decoder Instance
	if (m_SubStream)
	{
		Result			= m_SubStream->release();
		m_SubStream	= NULL;
	}

	if (m_BankSound)
	{
			// File is in Memory
			Result			= m_BankSound->release();
			m_BankSound	= NULL;
	}

	if (m_pMusicSystem->g_nMusicProfiling)
	{
		f1 = gEnv->pTimer->GetAsyncCurTime();
		CryLog(" Profile: DecoderInstance-BankRelease %s %f", m_pDecoder->m_FileInfo.sFileName.c_str(), (f1-f0)*1000.0f);
	}
}

void CFMODBankDecoderInstance::Init()
{
	if (m_SubStream)
		return;

	FMOD_RESULT Result = FMOD_OK;
	FMOD::System *pSystem = (FMOD::System *)gEnv->pSoundSystem->GetIAudioDevice()->GetSoundLibrary(); // unsafe!
	FMOD_MODE Mode = FMOD_CREATESTREAM | FMOD_LOWMEM;

	FMOD_CREATESOUNDEXINFO  ExInfo = {0}; 
	ExInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
	ExInfo.length = m_pDecoder->m_BufferLength;
	ExInfo.suggestedsoundtype = FMOD_SOUND_TYPE_FSB;
	ExInfo.initialsubsound = 0;
	ExInfo.decodebuffersize = 4096*4;

	float f0, f1;

	if (m_pMusicSystem->g_nMusicProfiling)
		f0 = gEnv->pTimer->GetAsyncCurTime();

	if(!m_BankSound && !m_pDecoder->m_BufferData)
	{
		// File gets streamed from disk
		{
			CSoundSystem const* const pSoundSystem = (CSoundSystem*)gEnv->pSoundSystem;
			(pSoundSystem->g_nFileAccess > 0) ? Mode |= FMOD_NONBLOCKING : 0;

			FRAME_PROFILER( "FMOD-CreateSound Music FSB-Decoder", GetISystem(), PROFILE_MUSIC );
			Result = pSystem->createSound(m_pDecoder->m_FileInfo.sFileName.c_str(), Mode, &ExInfo, &m_BankSound);

			if (Result != FMOD_OK || m_BankSound == NULL)
				return;
		}

	}
	else
	{
		// File is in Memory
		if(!m_BankSound && m_pDecoder->m_BufferData)
		{
			Mode |= FMOD_OPENMEMORY_POINT;

			{
				FRAME_PROFILER( "FMOD-CreateSound Music FSB-DecoderInstace", GetISystem(), PROFILE_MUSIC );
				Result = pSystem->createSound((const char *)m_pDecoder->m_BufferData, Mode, &ExInfo, &m_BankSound);
			}
		}
	}

	if (m_pDecoder->IsSoundAccessible(m_BankSound))
	{
		Result = m_BankSound->getSubSound(m_pDecoder->m_nSubSoundIndex, &m_SubStream);
		(Result == FMOD_OK) ? m_eAudioFileOpenState = eAFOS_READY : m_eAudioFileOpenState = eAFOS_ERROR;
	}
	else if(m_BankSound)
	{
		// Set the correct open state
		FMOD_OPENSTATE eOpenState;
		Result = m_BankSound->getOpenState(&eOpenState, NULL, NULL, NULL);

		switch (eOpenState)
		{
		case FMOD_OPENSTATE_LOADING:
			{
				m_eAudioFileOpenState = eAFOS_LOADING;
			}
			break;
		case FMOD_OPENSTATE_ERROR:
			{
				m_eAudioFileOpenState = eAFOS_ERROR;
			}
			break;
		}
	}

	if (m_pMusicSystem->g_nMusicProfiling)
	{
		f1 = gEnv->pTimer->GetAsyncCurTime();
		CryLog(" Profile: DecoderInstance Init %s %f", m_pDecoder->m_FileInfo.sFileName.c_str(), (f1-f0)*1000.0f);
	}
}


bool CFMODBankDecoderInstance::Seek0(const int nDelayInSamples)
{
	m_nPosInSamples = -nDelayInSamples;
	m_nStreamPosInSamples = m_nPosInSamples + 1; // enforce next seek
	m_bCopyFromLastFrame = false;
	
	return SeekPos(0);
}

//! Seek to pos in number of samples of pattern.
bool CFMODBankDecoderInstance::SeekPos(const int nSamplePos)
{
	bool bResult = true;

	if (nSamplePos != m_nStreamPosInSamples)
	{
		if (m_pDecoder->IsSoundSeekable(m_SubStream))
		{
			FMOD_RESULT const eResult = m_SubStream->seekData(nSamplePos);
			bResult                   = eResult == FMOD_OK;

			if (bResult)
			{
				m_nPosInSamples       = nSamplePos;
				m_nStreamPosInSamples = m_nPosInSamples;
			}
		}
		else
		{
			// This instance is not ready, save the sample pos so we go there later.
			// Still return success.
			m_nPosInSamples = nSamplePos;
		}
	}
	
	return bResult;
}

int CFMODBankDecoderInstance::GetPos()
{
	if (!m_pDecoder->IsSoundAccessible(m_SubStream))
		return -1;

	return m_nPosInSamples;
}

bool CFMODBankDecoderInstance::GetPCMData(signed long *pDataOut, int nSamples, bool bLoop)
{
	if (!m_pDecoder->IsSoundAccessible(m_SubStream) || m_pDecoder->m_FileInfo.nBitsPerSample <= 0)
		return false;

	FRAME_PROFILER( "Decoder", GetISystem(), PROFILE_MUSIC );	

	int nOfs = 0;

	if (m_nPosInSamples < 0)
	{
		if (-m_nPosInSamples >= nSamples)
		{
			memset(pDataOut, 0, nSamples * gEnv->pMusicSystem->GetBytesPerSample());
			m_nPosInSamples += nSamples;
			return true;
		}
		else
		{ 
			nOfs = -m_nPosInSamples;
			m_nPosInSamples = 0;
			nSamples -= nOfs;
			memset(pDataOut, 0, nOfs * gEnv->pMusicSystem->GetBytesPerSample());
		}
	}

	if (!SeekPos(m_nPosInSamples))
		return false;

	int nSamplesToRead;
	for (;;)
	{
		if ((m_nPosInSamples + nSamples) > (int)m_pDecoder->m_FileInfo.nSamples)
			nSamplesToRead = m_pDecoder->m_FileInfo.nSamples - m_nPosInSamples;
		else
			nSamplesToRead = nSamples;

		unsigned int nBytes = nSamples * (m_pDecoder->m_FileInfo.nBitsPerSample / 4);
		unsigned int nBytesRead = 0;

		FMOD_RESULT Result = m_SubStream->readData(&(pDataOut[nOfs]), nBytes, &nBytesRead);

		nSamplesToRead = nBytesRead / ((m_pDecoder->m_FileInfo.nBitsPerSample / 4));

		if (Result == FMOD_ERR_FILE_EOF)
		{
			int a = 1;(void)a;
			if (nBytesRead < (m_pDecoder->m_FileInfo.nSamples - m_nPosInSamples) )
			{
				nBytes = (m_pDecoder->m_FileInfo.nSamples - m_nPosInSamples) * (m_pDecoder->m_FileInfo.nBitsPerSample / 4);
				
				if (nBytes > (unsigned int)nSamples)
				{
					// something really bad happened here, lets quit the whole piece
					return false;
				}

				Result = m_SubStream->readData(&(pDataOut[nOfs]), nBytes, &nBytesRead);
			}
		}
		else
		{
			if (nBytes != nBytesRead)
			{
				int a = 0; (void)a;// catch me!
			}
		}

		// Here we need to have a FMOD_OK result otherwise there's no need to continue with this stream
		if (Result != FMOD_OK)
			return false;

		// FIXME: conversation of samplerate might be needed here
		m_nPosInSamples += nSamplesToRead;
		m_nStreamPosInSamples = m_nPosInSamples;

		if (nSamplesToRead == nSamples)
			break;

		nOfs += nSamplesToRead;

		if (!bLoop)
		{
			memset(&(pDataOut[nOfs]), 0, (nSamples-nSamplesToRead) * gEnv->pMusicSystem->GetBytesPerSample());
			return false;
			break;
		}

		nSamples -= nSamplesToRead;
		Seek0();
	}
	return true;
}

void CFMODBankDecoderInstance::GetMemoryUsage(class ICrySizer* pSizer)
{
	if (!pSizer->Add(*this))
		return;
}

//////////////////////////////////////////////////////////////////////////
bool const CFMODBankDecoderInstance::IsReleasable() const
{
	FMOD_OPENSTATE eOpenState;
	bool bDiskBusy = false;

	if (m_BankSound)
	{
		FMOD_RESULT const eResult = m_BankSound->getOpenState(&eOpenState, NULL, NULL, &bDiskBusy);
		
		if ((eOpenState != FMOD_OPENSTATE_READY && eOpenState != FMOD_OPENSTATE_ERROR) || bDiskBusy)
			return false;
	}

	if (m_SubStream)
	{
		FMOD_RESULT const eResult = m_SubStream->getOpenState(&eOpenState, NULL, NULL, &bDiskBusy);

		if ((eOpenState != FMOD_OPENSTATE_READY && eOpenState != FMOD_OPENSTATE_ERROR) || bDiskBusy)
			return false;
	}

	return true;
}

//////////////////////////////////////////////////////////////////////////
int unsigned const CFMODBankDecoderInstance::GetBufferFillPercentage() const
{
	if (m_pDecoder->IsSoundAccessible(m_SubStream))
	{
		unsigned int nBufferFillPercentage;
		m_SubStream->getOpenState(NULL, &nBufferFillPercentage, NULL, NULL);
		return nBufferFillPercentage;
	}

	return 0;
}
#endif 
